﻿using KPI.Entity;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Tsp;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace KPI.Api
{
    public class SecurityUtil
    {
        /**
    * 校验请求数据，签名为SM3不加盐
    *
    * @param requestDTO 所有请求参数
    * @param appSecret  服务端appSecret
    * @param sm4Encrypt 是否使用SM4加密
    * @return MutablePair<bool, JObject> 左侧为校验结果，右侧为解密后的请求参数
    * 校验成功，左侧为true，右侧为解密后的请求参数
    * 校验失败，左侧为false，右侧为加密后的错误返回信息
    */
        public static ComResult checkReqDataNoSalt(RequestDTO requestDTO, string appSecret, bool sm4Encrypt)
        {
            return checkReqData(requestDTO, appSecret, sm4Encrypt, false);
        }
        public static bool IsValidJson(string json)
        {
            try
            {
                JToken.Parse(json);
                return true;
            }
            catch (JsonReaderException)
            {
                return false;
            }
        }
        /**
         * 校验请求数据
         *
         * @param requestDTO   所有请求参数
         * @param appSecret    服务端appSecret
         * @param sm4Encrypt   是否使用SM4加密
         * @param signWithSalt 是否使用盐值
         * @return MutablePair<bool, JObject> 左侧为校验结果，右侧为解密后的请求参数
         * 校验成功，左侧为true，右侧为解密后的请求参数
         * 校验失败，左侧为false，右侧为加密后的错误返回信息
         */
        private static ComResult checkReqData(RequestDTO requestDTO, string appSecret,
                                              bool sm4Encrypt, bool signWithSalt)
        {
            ComResult comResult = new ComResult(0, "", null);

            string data = requestDTO.data;
            //log.info("请求入参（密文） == {}", data);
            string version = requestDTO.version;

            string appId = requestDTO.appid;
            string timestamp = requestDTO.timestamp;
            string sign = requestDTO.sign;

            // 验签
            bool signResult;

            signResult = checkSignNoSalt(appId, appSecret, data, timestamp, version, sign);


            if (signResult)
            { // 解密数据
                string decryptData;
                //log.info("是否SM4加密：" + sm4Encrypt);
                if (sm4Encrypt)
                {
                    decryptData = decryptSm4Base64(data, appSecret);
                }
                else
                {
                    decryptData = decodeBase64(data);
                }

                //log.info("请求入参（明文） == {}", decryptData);
                if (decryptData == null)
                { // 请求入参为空
                    return comResult;
                }
                bool valid = IsValidJson(decryptData);
                if (valid)
                {
                    JObject decryptReqData = JObject.Parse(decryptData);
                    comResult._object = decryptReqData;
                    return comResult;
                }
                else
                {
                    comResult.code = 1;
                    comResult.msg = "param format error.";
                    //log.error("param format error.");
                    return comResult;
                }
            }
            else
            {
                comResult.code = 2;
                comResult.msg = "verify signature failed.";
                //log.error("verify signature failed.");
                return comResult;
            }
        }

        /**
         * 加盐SM3验签
         *
         * @param appId     应用id
         * @param appSecret 应用密钥（加盐）
         * @param data      待签名数据
         * @param timestamp 随机数
         * @param version   版本号
         * @param reqSign   请求报文签名值
         * @return 验签结果
         */


        /**
         * 加盐SM3验签
         *
         * @param appId       应用id
         * @param appSecret   应用密钥（加盐）
         * @param requestDTO  响应报文
         * @return 验签结果
         */


        /**
         * 不加盐SM3验签
         *
         * @param appId     应用id
         * @param data      待签名数据
         * @param timestamp 随机数
         * @param version   版本号
         * @param reqSign   请求报文签名值
         * @return 验签结果
         */
        private static bool checkSignNoSalt(string appId, string appSecret, string data,
                                               string timestamp, string version, string reqSign)
        {
            if (string.IsNullOrEmpty(reqSign))
            {
                //log.error("传入的签名值为空 == {}", reqSign);
                return false;
            }
            string serveSign = signNoSalt(appId, appSecret, data, timestamp, version);
            bool equals = serveSign.Equals(reqSign);
            //log.debug("签名结果 == {}，传入的签名值 == {}，服务端签名值 == {}", equals, reqSign, serveSign);
            return equals;
        }

        /**
         * 加密返回参数，签名为SM3不加盐
         *
         * @param ret    返回参数
         * @param config 配置信息
         * @return JObject
         */
        public static JObject encryptRespDataNoSalt(Object ret, Tuple<string, string, bool> config)
        {
            return encryptRespData(ret, config.Item1, config.Item2, config.Item3, false);
        }

        /**
         * 加密返回参数，签名为SM3不加盐
         */
        public static JObject encryptRespDataNoSalt(Object ret, string appId, string appSecret, bool sm4Encrypt)
        {
            return encryptRespData(ret, appId, appSecret, sm4Encrypt, false);
        }

        /**
         * 加密Dictionary请求参数，默认签名为SM3加盐
         *
         * @param paramDictionary   请求参数
         * @param appId      应用id
         * @param appSecret  应用密钥
         * @param sm4Encrypt 是否使用SM4加密
         * @return 加密后的Dictionary参数
         */
        public static Dictionary<string, object> encryptReqData(Dictionary<string, object> paramDictionary, string appId, string appSecret, bool sm4Encrypt)
        {
            return encryptReqData(paramDictionary, appId, appSecret, sm4Encrypt, true);
        }

        /**
         * 加密Dictionary请求参数，签名为SM3不加盐
         */
        public static Dictionary<string, object> encryptReqDataNoSalt(Dictionary<string, object> paramDictionary, string appId, string appSecret, bool sm4Encrypt)
        {
            return encryptReqData(paramDictionary, appId, appSecret, sm4Encrypt, false);
        }

        /**
         * 加密返回参数
         *
         * @param ret          返回参数
         * @param appId        应用id
         * @param appSecret    应用密钥
         * @param signWithSalt 是否使用盐值
         * @return JObject
         */
        private static JObject encryptRespData(Object ret, string appId, string appSecret, bool sm4Encrypt, bool signWithSalt)
        {
            JObject JObject = new JObject();
            string data = encryptData(ret, appSecret, sm4Encrypt);
            ////log.info("请求回参（明文） == {}", ret); 避免重复的日志打印：aop有统一打印明文，非aop调用没有
            JObject.Add("data", data);
            string timestamp = DateTime.Now.Ticks + "";
            JObject.Add("timestamp", timestamp);
            string version = "1.0";
            string _sign;

            _sign = signNoSalt(appId, appSecret, data, timestamp, version);


            JObject.Add("sign", _sign);
            return JObject;
        }

        /**
         * 加密Dictionary请求参数
         *
         * @param paramDictionary     请求参数
         * @param appId        应用id
         * @param appSecret    应用密钥
         * @param sm4Encrypt   是否使用SM4加密
         * @param signWithSalt 是否使用盐值
         * @return 加密后的Dictionary参数
         */
        private static Dictionary<string, object> encryptReqData(Dictionary<string, object> paramDictionary, string appId,
                                                          string appSecret, bool sm4Encrypt, bool signWithSalt)
        {
            Dictionary<string, object> encryptDictionary = new Dictionary<string, object>();
            encryptDictionary.Add("appid", appId);
            string data = encryptData(paramDictionary, appSecret, sm4Encrypt);
            encryptDictionary.Add("data", data);

            object currenttimemillis = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
            string timestamp = currenttimemillis + ""; //"1698139029626";// 

            encryptDictionary.Add("timestamp", timestamp);
            string version = "1.0";
            string _sign;

            _sign = signNoSalt(appId, appSecret, data, timestamp, version);

            encryptDictionary.Add("sign", _sign);
            encryptDictionary.Add("version", version);
            return encryptDictionary;
        }

        /**
         * JSON类型参数data加密
         *
         * @param data       参数data
         * @param appSecret  应用密钥
         * @param sm4Encrypt SM4加密
         * @return string
         */
        private static string encryptData(Object data, string appSecret, bool sm4Encrypt)
        {
            string base64Data = encodeBase64(data);
            if (sm4Encrypt == true)
            {
                // 数据是否SM4加密
                return encryptSm4(base64Data, appSecret);
            }
            return base64Data;
        }

        /**
         * 不加盐SM3签名
         *
         * @param appId     应用id
         * @param data      待签名数据
         * @param timestamp 随机数
         * @param version   版本号
         * @return 签名
         */
        private static string signNoSalt(string appId, string appSecret, string data, string timestamp, string version)
        {
            string _str = "appid=" + appId + "&data=" + data + "&timestamp=" + timestamp;
            string stringSignTemp = _str + "&key=" + appSecret + "&version=" + version;
            //log.debug("签名字符串：{}", stringSignTemp);
            return Sm3Crypto.ToSM3HexStr(stringSignTemp);
        }

        /**
         * 加盐SM3签名
         *
         * @param appId     应用id
         * @param appSecret 应用密钥（加盐）
         * @param data      待签名数据
         * @param timestamp 随机数
         * @param version   版本号
         * @return 签名
         */


        /**
         * 数据base64加密
         *
         * @param data 待加密数据
         * @return 加密后的数据
         */
        private static string encodeBase64(string data)
        {
            if (data == null)
            {
                return null;
            }
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
        }

        /**
         * JSON数据base64加密
         *
         * @param data 待加密数据
         * @return 加密后的数据
         */
        private static string encodeBase64(JObject data)
        {
            if (data == null)
            {
                return null;
            }
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonAsString(data)));
        }

        public static string JsonAsString(object vale)
        {
            return Newtonsoft.Json.JsonConvert.SerializeObject(vale);
        }

        private static string encodeBase64(Object data)
        {
            if (data == null)
            {
                return null;
            }
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonAsString(data)));
        }

        /**
         * Dictionary数据base64加密
         *
         * @param data 待加密数据
         * @return 加密后的数据
         */
        private static string encodeBase64(Dictionary<string, object> data)
        {
            if (data == null)
            {
                return null;
            }
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonAsString(data)));
            //return Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonAsString(data));
        }

        /**
         * 数据base64解密
         *
         * @param data 待解密数据
         * @return 解密后的数据
         */
        private static string decodeBase64(string data)
        {
            if (data == null)
            {
                return null;
            }
            byte[] bytes = Convert.FromBase64String(data);
            return Encoding.UTF8.GetString(bytes);
            //return Base64.decodeStr(data);
        }

        /**
         * SM4加密
         *
         * @param data      待加密数据
         * @param appSecret 密钥
         * @return 加密后的数据
         */
        private static string encryptSm4(string data, string appSecret)
        {
            if (data == null)
            {
                return data;
            }
            if (appSecret == null)
            {
                throw new SecurityException("appSecret is null");
            }
            //SM4Engine sm4 = new SM4Engine();
            SM4Util sm4Util = new SM4Util(appSecret);
            return sm4Util.Encrypt_ECB(data);
            //SymmetricCrypto crypto = getCrypto(appSecret);
            //return crypto.encryptHex(data);
        }

        /**
         * SM4解密后再进行base64解密
         *
         * @param data      待解密数据
         * @param appSecret 密钥
         * @return 解密后的数据
         */
        public static string decryptSm4Base64(string data, string appSecret)
        {
            string s = decryptSm4(data, appSecret);
            return decodeBase64(s);
        }

        /**
         * SM4解密
         *
         * @param data      待解密数据
         * @param appSecret 密钥
         * @return 解密后的数据
         */
        public static string decryptSm4(string data, string appSecret)
        {
            if (data == null)
            {
                return null;
            }
            if (appSecret == null)
            {
                throw new SecurityException("数据解密异常：appSecret is null");
            }
            try
            {
                SM4Util sm4Util = new SM4Util(appSecret);
                return sm4Util.Decrypt_ECB(data);
                //SymmetricCrypto crypto = getCrypto(appSecret);
                //return crypto.decryptStr(data);
            }
            catch (SecurityException e)
            {
                throw new SecurityException("数据解密异常：SM4 decrypt error");
            }
        }

        //private static Sm3Crypto getCrypto(string appSecret)
        //{
        //    string substring = appSecret.Substring(0, 16);
        //    return SmUtil.sm4(Encoding.UTF8.GetBytes(substring));
        //}
    }
}
